package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/goburrow/serial"
	"github.com/kataras/iris/v12"
	"github.com/kataras/iris/v12/context"
	"github.com/zing-dev/general-protocol-lib/cfg"
	"github.com/zing-dev/general-protocol-lib/log"
	"github.com/zing-dev/general-protocol-lib/protocol/common"
	"github.com/zing-dev/general-protocol-lib/source/zqwl-1dxrr16"
	"io"
	"net/http"
	"net/url"
	"time"
)

const SectionName = "ZQWL继电器客户端"

type Config struct {
	Host         string   `json:"host" comment:"当前继电器客户端地址,如http://10.0.0.10:5841,不能为127.0.0.1或localhost"`
	host         *url.URL `ini:"-"`
	RelaysServer string   `json:"-" comment:"继电器服务端地址 如http://10.0.0.10:5840"`
	server       *url.URL `ini:"-"`
	SerialPort   string   `json:"serial_port" comment:"串口地址,如COM19"`
	BaudRate     int      `json:"baud_rate" ini:"-"`
	Start        int      `json:"start" comment:"继电器开始路数,如1"`
	End          int      `json:"end" comment:"继电器结束路数,如16"`
	Name         string   `json:"name" ini:"-"`   // 设备名
	Size         byte     `json:"size" ini:"-"`   // 继电器路数
	Status       []int    `json:"status" ini:"-"` // 继电器状态
}

func newConfig() *Config {
	config := new(Config)
	cfg.New().Register(func(c *cfg.Config) {
		section := c.File.Section(SectionName)
		if section.Comment == "" {
			section.Comment = fmt.Sprintf("项目名: %s", SectionName)
		}
		if len(section.Keys()) == 0 {
			err := section.ReflectFrom(config)
			if err != nil {
				log.L.Fatal(fmt.Sprintf("%s 反射失败: %s", SectionName, err))
			}
			c.Save()
			log.L.Fatal("请修改配置文件")
		}
		err := section.MapTo(config)
		if err != nil {
			log.L.Fatal(fmt.Sprintf("映射错误: %s", err))
		}
		if config.Host == "" {
			log.L.Fatal("请输入当前主机地址")
		}
		h, err := url.Parse(config.Host)
		if err != nil {
			log.L.Fatal(fmt.Sprintf("校验当前主机 %s 失败: %s", config.Host, err))
		}
		config.host = h
		if config.RelaysServer == "" {
			log.L.Fatal("请输入继电器注册服务地址")
		}
		s, err := url.Parse(config.RelaysServer)
		if err != nil {
			log.L.Fatal(fmt.Sprintf("校验当前主机 %s 失败: %s", config.Host, err))
		}
		config.server = s
		if config.SerialPort == "" {
			log.L.Fatal("请输入继电器串口")
		}
		if config.Start == 0 || config.End == 0 || config.Start >= config.End {
			log.L.Fatal("请输入正确的继电器起始路数")
		}
	}).Load()
	return config
}

type Response struct {
	ctx    context.Context
	Msg    string `json:"msg"`
	Code   int    `json:"code"`
	Status bool   `json:"status"`
	Data   any    `json:"data,omitempty"`
}

func (r Response) IsOk() bool {
	return r.Status || r.Code == 0
}

func (r Response) success() {
	r.Status = true
	if r.Msg == "" {
		r.Msg = "操作成功"
	}
	_, _ = r.ctx.JSON(r)
}

func (r Response) fail() {
	r.Status = false
	_, _ = r.ctx.JSON(r)
}

func main() {
	log.Init()
	config := newConfig()
	web := iris.New()
	rtuConfig := zqwl_1dxrr16.Config{
		SlaveId:   1,
		ResetTime: 10,
		Config: serial.Config{
			Address:  config.SerialPort,
			DataBits: 8,
			StopBits: 1,
			Timeout:  time.Second,
		},
	}
	relay, err := zqwl_1dxrr16.NewRTUClientHandler(rtuConfig)
	if err != nil {
		log.L.Fatal(fmt.Sprintf("连接继电器失败: %s", err))
	}

	info, err := relay.Info()
	if err != nil {
		log.L.Fatal("获取继电器信息失败: ", err)
		return
	} else {
		config.Name = info.Version
		config.BaudRate = int(info.BaudRate)
	}
	log.L.Info("version: ", info.Version)
	log.L.Info("baud-rate: ", info.BaudRate)

	status, err := relay.Status()
	if err != nil {
		log.L.Fatal(fmt.Sprintf("获取继电器状态失败: %s", err))
	}
	config.Size = byte(len(status))
	go func() {
		client := http.Client{Timeout: time.Second}

		u := fmt.Sprintf("%s/api/relay", config.RelaysServer)
		count := 0
		for {
			res := Post(&client, u, config)
			if res.IsOk() {
				log.L.Info("注册继电器成功")
				break
			}
			log.L.Error(res.Msg)
			if count > 100 {
				break
			}
			count++
			time.Sleep(time.Second * time.Duration(count))
		}

		for {
			log.L.Info(fmt.Sprintf("Ping %s ...", config.RelaysServer))
			status, err := relay.Status()
			if err != nil {
				log.L.Error(fmt.Sprintf("获取继电器状态失败: %s", err))
				continue
			}
			config.Status = make([]int, len(status))
			for i, s := range status {
				config.Status[i] = int(s)
			}
			res := Put(&client, u, config)
			if !res.IsOk() {
				log.L.Error(fmt.Sprintf("推送继电器状态失败: %s", res.Msg))
			}
			time.Sleep(time.Second * 30)
		}
	}()
	r := web.Party("/api/relay", func(ctx context.Context) {
		log.L.Info(fmt.Sprintf("主机 %s 访问接口 %s ", ctx.RemoteAddr(), ctx.Request().RequestURI))
		ctx.Next()
	})
	{
		r.Get("/on-all", func(c context.Context) {
			err := relay.OnAll()
			if err != nil {
				Response{ctx: c, Msg: fmt.Sprintf("打开继电器失败: %s", err)}.fail()
				return
			}
			Response{ctx: c}.success()
		})

		r.Get("/on-all/{reset_time}", func(c context.Context) {
			resetTime, err := c.Params().GetInt("reset_time")
			if err != nil {
				Response{ctx: c, Msg: fmt.Sprintf("获取继电器重置失败: %s", err)}.fail()
				return
			}
			err = relay.OnAll()
			if err != nil {
				Response{ctx: c, Msg: fmt.Sprintf("打开继电器失败: %s", err)}.fail()
				return
			}
			time.AfterFunc(time.Second*time.Duration(resetTime), func() {
				err := relay.OffAll()
				if err != nil {
				}
			})
			Response{ctx: c}.success()
		})

		r.Get("/on/{branch}", func(c context.Context) {
			branch, err := c.Params().GetInt("branch")
			if err != nil {
				Response{ctx: c, Msg: fmt.Sprintf("获取继电器路数失败: %s", err)}.fail()
				return
			}
			err = relay.On(byte(branch))
			if err != nil {
				Response{ctx: c, Msg: fmt.Sprintf("打开继电器失败: %s", err)}.fail()
				return
			}
			Response{ctx: c}.success()
		})

		r.Get("/on/{branch}/{reset_time}", func(c context.Context) {
			branch, err := c.Params().GetInt("branch")
			if err != nil {
				Response{ctx: c, Msg: fmt.Sprintf("获取继电器路数失败: %s", err)}.fail()
				return
			}
			resetTime, err := c.Params().GetInt("reset_time")
			if err != nil {
				Response{ctx: c, Msg: fmt.Sprintf("获取继电器重置失败: %s", err)}.fail()
				return
			}
			err = relay.On(byte(branch))
			if err != nil {
				Response{ctx: c, Msg: fmt.Sprintf("打开继电器失败: %s", err)}.fail()
				return
			}
			time.AfterFunc(time.Second*time.Duration(resetTime), func() {
				err := relay.Off(byte(branch))
				if err != nil {
				}
			})
			Response{ctx: c}.success()
		})

		r.Get("/off-all", func(c context.Context) {
			err := relay.OffAll()
			if err != nil {
				Response{ctx: c, Msg: fmt.Sprintf("关闭继电器失败: %s", err)}.fail()
				return
			}
			Response{ctx: c}.success()
		})

		r.Get("/off-all/{reset_time}", func(c context.Context) {
			resetTime, err := c.Params().GetInt("reset_time")
			if err != nil {
				Response{ctx: c, Msg: fmt.Sprintf("获取继电器重置失败: %s", err)}.fail()
				return
			}
			err = relay.OffAll()
			if err != nil {
				Response{ctx: c, Msg: fmt.Sprintf("打开继电器失败: %s", err)}.fail()
				return
			}
			time.AfterFunc(time.Second*time.Duration(resetTime), func() {
				err := relay.OnAll()
				if err != nil {
				}
			})
			Response{ctx: c}.success()
		})

		r.Get("/off/{branch}", func(c context.Context) {
			branch, err := c.Params().GetInt("branch")
			if err != nil {
				Response{ctx: c, Msg: fmt.Sprintf("获取继电器路数失败: %s", err)}.fail()
			}
			err = relay.Off(byte(branch))
			if err != nil {
				Response{ctx: c, Msg: fmt.Sprintf("打开继电器失败: %s", err)}.fail()
				return
			}
			Response{ctx: c}.success()
		})

		r.Get("/status", func(c context.Context) {
			status, err := relay.Status()
			if err != nil {
				Response{ctx: c, Msg: fmt.Sprintf("获取继电器状态失败: %s", err)}.fail()
				return
			}
			var data = make([]int, len(status))
			for i, b := range status {
				data[i] = int(b)
			}
			Response{ctx: c, Data: data}.success()
		})

		r.Get("/info", func(c context.Context) {
			info, err := relay.Info()
			if err != nil {
				Response{ctx: c, Msg: fmt.Sprintf("获取继电器信息失败: %s", err)}.fail()
				return
			}
			Response{ctx: c, Data: info}.success()
		})

		r.Get("/setting", func(c context.Context) {
			rtuConfig.Address = c.URLParamDefault("address", "COM19")
			relay.Close()
			r, err := zqwl_1dxrr16.NewRTUClientHandler(rtuConfig)
			if err != nil {
				Response{ctx: c, Msg: fmt.Sprintf("连接继电器失败: %s", err)}.fail()
				return
			}
			relay = r
			Response{ctx: c}.success()
		})

		r.Get("/run", func(c context.Context) {
			relay.Close()
			r, err := zqwl_1dxrr16.NewRTUClientHandler(rtuConfig)
			if err != nil {
				Response{ctx: c, Msg: fmt.Sprintf("连接继电器失败: %s", err)}.fail()
				return
			}
			relay = r
			Response{ctx: c}.success()
		})

		r.Get("/close", func(c context.Context) {
			relay.Close()
			Response{ctx: c, Data: info}.success()
		})

	}

	err = web.Listen(config.host.Host)
	if err != nil {
		return
	}
}

func handle(resp *http.Response, response *Response, err error) *Response {
	if err != nil {
		response.Msg = err.Error()
		return response

	}
	if resp.StatusCode != http.StatusOK {
		response.Msg = fmt.Sprintf("返回状态码<%d>异常: %s", resp.StatusCode, resp.Status)
		return response
	}
	data, err := io.ReadAll(resp.Body)
	if err != nil {
		response.Msg = err.Error()
		return response
	}
	err = json.Unmarshal(data, response)
	if err != nil {
		response.Msg = err.Error()
		return response
	}
	return response
}

func Post(client *http.Client, url string, config *Config) *Response {
	response := new(Response)
	data, err := json.Marshal(config)
	if err != nil {
		response.Msg = err.Error()
		return response
	}
	res, err := client.Post(url, common.ContentTypeJson, bytes.NewBuffer(data))
	return handle(res, response, err)
}

func Put(client *http.Client, url string, config *Config) *Response {
	response := new(Response)
	data, err := json.Marshal(config)
	if err != nil {
		response.Msg = fmt.Sprintf("marshal: %s", err)
		return response
	}
	request, err := http.NewRequest("PUT", url, bytes.NewBuffer(data))
	if err != nil {
		response.Msg = err.Error()
		response.Msg = fmt.Sprintf("new request: %s", err)
		return response
	}
	request.Header.Set("Content-Type", common.ContentTypeJson)
	res, err := client.Do(request)
	return handle(res, response, err)
}

func Get(client *http.Client, url string) *Response {
	response := new(Response)
	resp, err := client.Get(url)
	return handle(resp, response, err)
}
